--- 模块功能：DTU主逻辑
-- @author openLuat
-- @module default
-- @license MIT
-- @copyright openLuat
-- @release 2018.12.27
-- require "cc"
require "pm"
require "db"
require "iic"
require "sms"
require "link"
require "pins"
require "misc"
require "mqtt"
require "utils"
require "lbsLoc"
require "socket"
require "audio"
require "httpv2"
require "common"
require "create"
require "tracker"
-- 自定义库
require "power"
require "pcf8563"
require "key5"
require "lcd"
require "usrdata"
require "tools"
--require "AHT10"
module(..., package.seeall)
-- 电源保持
power.openMasterLock()
-- 判断模块类型
local ver = rtos.get_version():upper()
local is8910 = ver:find("8910")
local isTTS = ver:find("TTS") or ver:find("8955F")
-- 串口缓冲区最大值
local SENDSIZE = _G.is8955 and 1460 or 8192
-- 串口写空闲
local writeIdle = {true, true, true, true}
-- 串口读缓冲区
local recvBuff, writeBuff = {{}, {}, {}, {}}, {{}, {}, {}, {}}
-- 串口流量统计
local flowCount, timecnt = {0, 0, 0, 0}, 1
-- 定时采集任务的初始时间
local startTime = {0, 0, 0}
-- 定时采集任务缓冲区
local sendBuff = {{}, {}, {}, {}}
-- 基站定位坐标
local lbs = {lat, lng}
-- 配置文件
local dtu = {
    param_ver = 0, -- 参数版本
    defchan = 1, -- 默认监听通道
    host = "", -- 自定义参数服务器
    passon = 0, -- 透传标志位，1透传
    plate = 0, -- 识别码标志位
    convert = 0, -- hex转换标志位
    reg = 1, -- 登陆注册包
    flow = 0, -- 流量监控
    fota = 1, -- 远程升级
    uartReadTime = 500, -- 串口读超时
    netReadTime = 100, -- 网络读超时
    pwrmod = "normal",
    password = "820310",
    upprot = {}, -- 上行自定义协议
    dwprot = {}, -- 下行自定义协议
    apn = {nil, nil, nil}, -- 用户自定义APN
    cmds = {{}, {}}, -- 自动采集任务参数
    pins = {"", "", ""}, -- 用户自定义IO: netled,netready,rstcnf,
    conf = {{"tcp","HB","300","yuanfen360.cn","4017",3,"","","",""}, {}, {}, {}, {}, {}, {}}, -- 用户通道参数
    preset = {
        number = "",
        delay = 1,
        smsword = "SMS_UPDATE"
    }, -- 用户预定义的来电电话,延时时间,短信关键字
    uconf = {{1, 115200, 8, uart.PAR_NONE, uart.STOP_1}, {2, 115200, 8, uart.PAR_NONE, uart.STOP_1}, {3, 9600, 8, uart.PAR_NONE, uart.STOP_1}}, -- 串口配置表
    gps = {
        fun = {"", "115200", "0", "5", "1", "json", "100", ";", "60"}, -- 用户捆绑GPS的串口,波特率，功耗模式，采集间隔,采集方式支持触发和持续, 报文数据格式支持 json 和 hex，缓冲条数,分隔符,状态报文间隔
        pio = {"", "", "", "", "0", "16"} -- 配置GPS用到的IO: led脚，vib震动输入脚，ACC输入脚,内置电池充电状态监视脚,adc通道,分压比
    },
    warn = {
        gpio = {},
        adc0 = {},
        adc1 = {},
        vbatt = {}
    },
    task = {} -- 用户自定义任务列表
}
-- 配置默认参数，防止文件读取失败
local udtu = {
    ontime = 2, -- 持续开机时间,？分钟
    alarm = {0, 60}, -- 定时器，
    dtype = 2, -- 设备类型 1:连利水表,2:海峰流量计,3:道胜流量计,4,山东建恒光电直读水表 默认地址 01，11:恒瑞水位传感器
    dsn = "2022init", -- 设备地址
    ntype = 0, -- 网络协议 0:不使能,1:szy206
    nsn = "2022init", -- 网络协议中 需要的号码，比如SIM卡号等
    addr=1,
}
-- 获取参数版本
io.getParamVer = function() return dtu.param_ver end
---------------------------------------------------------- 开机读取保存的配置文件 ----------------------------------------------------------
-- 自动任务采集
local function autoSampl(uid, t)
    while true do
        sys.waitUntil("AUTO_SAMPL_" .. uid)
        for i = 2, #t do
            local str = t[i]:match("function(.+)end")
            if not str then
                if t[i] ~= "" then write(uid, (t[i]:fromHex())) end
            else
                local res, msg = pcall(loadstring(str))
                if res then sys.publish("NET_SENT_RDY_" .. uid, msg) end
            end
            sys.wait(tonumber(t[1]))
        end
    end
end
-- 加载rtu预置的配置文件
local cfg = db.new("/lua/irtu.cfg")
if cfg then
    local sheet = cfg:export()
    if type(sheet) == "table" and sheet.uconf then
        dtu = sheet
        if dtu.apn and dtu.apn[1] and dtu.apn[1] ~= "" then link.setAPN(unpack(dtu.apn)) end
        if not _G.is8955 and dtu.auth and dtu.auth[2] and tonumber(dtu.auth[1]) then link.setAuthApn(tonumber(dtu.auth[1]), dtu.auth[2], dtu.auth[3], dtu.auth[4]) end
        if dtu.cmds and dtu.cmds[1] and tonumber(dtu.cmds[1][1]) then sys.taskInit(autoSampl, 1, dtu.cmds[1]) end
        if dtu.cmds and dtu.cmds[2] and tonumber(dtu.cmds[2][1]) then sys.taskInit(autoSampl, 2, dtu.cmds[2]) end
        if tonumber(dtu.nolog) ~= 1 then _G.LOG_LEVEL = log.LOG_SILENT end
    end
end
-- 加载用户初始配置文件
local usr_cfg = db.new("/lua/usr.cfg")
if usr_cfg then
    local usheet = usr_cfg:export()
    if type(usheet) == "table" and usheet.ontime and usheet.alarm then
        udtu = usheet
    else
        usr_cfg.import(udtu)
    end
end
if tonumber(udtu.dtype)==0 then
    dtu.passon=1 -- 开启透传模式
end
-- 防止更改主连接
-- dtu.conf[1] = {"tcp","HB","300","yuanfen360.cn","4017",1,"","","",""}
dtu.errcode={}
pios={} --可以控制的io,这里不再使用
-- 传递配置文件
create.setDtu(dtu, udtu)
-- 设置默认监听通道
create.setchannel(dtu.defchan or 1)
-------------------------------------------------------锁定电源并设置定时关机--------------------------------------
local function setClock()
    sys.taskInit(function()
        h = tonumber(udtu.alarm[1]) or 0
        m = tonumber(udtu.alarm[2]) or 60
        while not pcf8563.setAlarm(h, m) do
            lcd.DTU_ALARM ="ERROR"
            sys.wait(1000)
        end
        lcd.DTU_ALARM = pcf8563.readAlarm()
        --if udtu.beep and udtu.beep==1 then sys.publish("SYS_BEEP",1000) end
    end)
end
lcd.LINE_BAR = (udtu.ontime or 3) * 60
sys.taskInit(function()
    local ont = tonumber(udtu.ontime) or 3
    setClock() -- 开机设置闹钟
    if ont ~= 0 then -- 0 不关机
        local result = sys.waitUntil("RTU_POWER_OFF", (ont * 60 * 1000)) -- 等待系统信号关闭，或者超时3分钟
        if result then
            lcd.LINE_BAR = 0
            lcd.USR_STATU = ''
            sys.wait(3000) -- 延时关机
        end
        local delay = 120
        while lcd.LCD_ACTIVE==2 and delay>0 do
            sys.wait(1000)
            delay=delay-1
        end
        power.closeMaster()
    end
end)
-- 闹钟AF=1后重新设置闹钟--
sys.taskInit(function()
    local count,sum = 0,((tonumber(udtu.alarm[2]) or 60) * 60)
    while true do
        count = count + 1
        sys.wait(1*1000)
        if (pcf8563.readAlarmFlag()) then
            sys.publish("DTU_ALARM_ON")
            log.warn("default",'闹钟启动')
            count = 0
            sys.wait(1000)
            setClock()
        end
        log.warn("default",'等待闹钟')
        if count >sum  then
            count = 0
            sys.publish("DTU_ALARM_ON")
            log.warn("default",'延时启动')
        end
    end
end)
-- 重置DTU
function resetConfig(msg)
    if msg ~= cpu.INT_GPIO_POSEDGE then
        db.remove(cfg)
        if io.exists("/alikey.cnf") then os.remove("/alikey.cnf") end
        if io.exists("/qqiot.dat") then os.remove("/qqiot.dat") end
        if io.exists("/bdiot.dat") then os.remove("/bdiot.dat") end
        sys.restart("软件恢复出厂默认值: OK")
    end
end
-- NETLED指示灯任务
local function blinkPwm(ledPin, light, dark)
    ledPin(1)
    sys.wait(light)
    ledPin(0)
    sys.wait(dark)
end
local function netled(led)
    local netready = pins.setup(22, 0)
    local ledpin = pins.setup(led, 0)
    while true do
        -- GSM注册中
        while not link.isReady() do blinkPwm(ledpin, 100, 100) end
        while link.isReady() do
            if create.getDatalink() then
                netready(1)
                blinkPwm(ledpin, 200, 1800)
            else
                netready(0)
                blinkPwm(ledpin, 500, 500)
            end
        end
        sys.wait(100)
    end
end
sys.taskInit(netled, 4)
-- 配置串口
if dtu.pwrmod ~= "energy" then pm.wake("mcuUart.lua") end
-- 串口写数据处理
function write(uid, str)
    uid = tonumber(uid)
    if not str or str == "" or not uid then return end
    if uid == uart.USB then return uart.write(uart.USB, str) end
    if str ~= true then
        for i = 1, #str, SENDSIZE do table.insert(writeBuff[uid], str:sub(i, i + SENDSIZE - 1)) end
        log.warn("uart" .. uid .. ".write data length:", writeIdle[uid], #str)
    end
    if writeIdle[uid] and writeBuff[uid][1] then
        if 0 ~= uart.write(uid, writeBuff[uid][1]) then
            log.info("UART_" .. uid .. " write:", #writeBuff[uid][1], (writeBuff[uid][1]:sub(1, 100):toHex()))
            table.remove(writeBuff[uid], 1)
            writeIdle[uid] = false
            --log.warn("UART_" .. uid .. " writing ...")
        end
    end
end

local function writeDone(uid)
    if #writeBuff[uid] == 0 then
        writeIdle[uid] = true
        sys.publish("UART_" .. uid .. "_WRITE_DONE")
        --log.warn("UART_" .. uid .. " write done!")
    else
        writeIdle[uid] = false
        uart.write(uid, table.remove(writeBuff[uid], 1))
        --log.warn("UART_" .. uid .. " writing ...")
    end
end

-- DTU配置工具默认的方法表
cmd = {}
cmd.config = {
    ["pipe"] = function(t, num)
        dtu.conf[tonumber(num)] = t
        return "OK"
    end, -- "1"-"7" 为通道配置
    ["A"] = function(t)
        dtu.apn = t
        return "OK"
    end, -- APN 配置
    ["B"] = function(t)
        dtu.cmds[tonumber(table.remove(t, 1)) or 1] = t
        return "OK"
    end, -- 自动任务下发配置
    ["auth"] = function(t)
        dtu.auth = t
        return "OK"
        
    end, -- 设置专网APN
    ["pins"] = function(t)
        dtu.pins = t
        return "OK"
    end, -- 自定义GPIO
    ["host"] = function(t)
        dtu.host = t[1]
        return "OK"
    end, -- 自定义参数升级服务器
    ["0"] = function(t) -- 保存配置参数
        local password = ""
        dtu.passon, dtu.plate, dtu.convert, dtu.reg, dtu.param_ver, dtu.flow, dtu.fota, dtu.uartReadTime, dtu.pwrmod, password, dtu.netReadTime, dtu.nolog = unpack(t)
        if password == dtu.password or dtu.password == "" or dtu.password == nil then
            dtu.password = password
            cfg:import(dtu)
            if not _G.is8955 and dtu.auth and tonumber(dtu.auth[1]) and dtu.auth[2] then link.setAuthApn(tonumber(dtu.auth[1]), dtu.auth[2], dtu.auth[3], dtu.auth[4]) end
            sys.timerStart(sys.restart, 3000, "Setting parameters have been saved!")
            return "SAVE OK"
        else
            return "PASSWORD ERROR"
        end
    end,
    ["save"] = function(t) -- 保存用户配置参数
        if t[1] == dtu.password or dtu.password == "" or dtu.password == nil then
            dtu.password = t[1]
            usr_cfg:import(udtu)
            cfg:import(dtu)
            sys.timerStart(sys.restart, 3000, "Setting parameters have been saved!")
            return "OK"
        else
            return "PASSWORD ERROR"
        end
    end,
    ["8"] = function(t) -- 串口配置默认方法
        local tmp = "1200,2400,4800,9600,14400,19200,28800,38400,57600,115200,230400,460800,921600"
        if t[1] and t[2] and t[3] and t[4] and t[5] then
            if ("1,2"):find(t[1]) and tmp:find(t[2]) and ("7,8"):find(t[3]) and ("0,1,2"):find(t[4]) and ("0,2"):find(t[5]) then
                dtu.uconf[tonumber(t[1])] = t
                return "OK"
            else
                return "ERROR"
            end
        end
    end,
    ["9"] = function(t) -- 预置白名单
        dtu.preset.number, dtu.preset.delay, dtu.preset.smsword = unpack(t)
        dtu.preset.delay = tonumber(dtu.preset.delay) or 1
        return "OK"
    end,
    ["readconfig"] = function(t) -- 读取整个DTU的参数配置
        if t[1] == dtu.password or dtu.password == "" or dtu.password == nil then
            return cfg:export("string")
        else
            return "PASSWORD ERROR"
        end
    end,
    ["readusrconfig"] = function(t) -- 读取udtu的参数配置
        if t[1] == dtu.password or dtu.password == "" or dtu.password == nil then
            return usr_cfg:export("string")
        else
            return "PASSWORD ERROR"
        end
    end,
    ["writeudtu"] = function(t,s) -- 写入udtu的参数配置
        local str = s:match("(.+)\r\n") and s:match("(.+)\r\n"):sub(18, -1) or s:sub(18, -1)
        log.warn("write_cfg:",str)
        local dat, result, errinfo = json.decode(str)
        if (errinfo) then log.error("write_cfg", "转换json错误:" .. errinfo) end
        if result then
            if dtu.password == dat.password or dtu.password == "" or dtu.password == nil then
                usr_cfg:import(str)
                usrdata.clear()
                if udtu.alarm[1]~= tonumber(dat.alarm[1]) or udtu.alarm[2]~= tonumber(dat.alarm[2]) then
                    udtu.alarm = dat.alarm
                    setClock()
                end
                sys.timerStart(sys.restart, 3000, "Setting parameters have been saved!")
                return "Setting parameter OK"
            else
                return "PASSWORD ERROR"
            end
        else
            return "JSON ERROR"
        end
    end,
    ["writeconfig"] = function(t, s) -- 读取整个DTU的参数配置
        local str = s:match("(.+)\r\n") and s:match("(.+)\r\n"):sub(20, -1) or s:sub(20, -1)
        log.warn("write_cfg:",str)
        local dat, result, errinfo = json.decode(str)
        if (errinfo) then log.error("write_cfg", "转换json错误:" .. errinfo) end
        if result then
            if dtu.password == dat.password or dtu.password == "" or dtu.password == nil then
                cfg:import(str)
                if dat.auth and tonumber(dat.auth[1]) and dat.auth[2] then link.setAuthApn(tonumber(dat.auth[1]), dat.auth[2], dat.auth[3], dat.auth[4]) end
                sys.timerStart(sys.restart, 5000, "Setting parameters have been saved!")
                return "OK"
            else
                return "PASSWORD ERROR"
            end
        else
            return "JSON ERROR"
        end
    end
}
cmd.rrpc = {
    ----------------------------自定义命令开始--------------------------
    ["getReg"]=function (t)
        local reg = tonumber(t[1])
        local addr = tonumber(t[2])
        local val = modbus_slave.REG_T[reg][addr]
        return string.format("reg[%02X][%04X]=%04X",reg,addr,val)
    end,
    ["senduart"] = function(t) -- 远程发送串到串口 rrpc,senduart,3,0103***
        local uid = tonumber(t[1])
        local msg = t[2]
        msg = msg and msg:fromHex()
        sys.publish("UART_SENT_RDY_"..uid, uid, msg)
        sys.subscribe("UART_READ_DONE_NEXT_"..uid,function (s)
            sys.publish("NET_SENT_RDY_1", s)
        end)
        return "DEVICE SEND OK"
    end,
    ["getusrdata"] = function(t) return "rrpc,getusrdata," .. json.encode(usrdata.getVal()) end, -- 获取当前数据
    ["cleardata"] = function(t) 
        usrdata.clear() 
        return "rrpc,cleardata,OK" 
    end,
    ["sendData"] = function(t) 
        sys.publish("DTU_USRDATA_START")
        return "rrpc,sendData,OK" 
    end,
    ["usrdata_issend"] = function(t) 
        sys.publish("DTU_USRDATA_ISSEND")
    end, 
    ["setdtype"] = function(t)
        udtu.dtype = tonumber(t[1])
        usr_cfg:import(udtu)
        sys.timerStart(sys.restart, 3000, "Set pram reboot!")
        return "rrpc,setdtype,OK"
    end,
    ["getdtype"] = function(t) return "rrpc,getdtype," .. udtu.dtype end, -- 获取网络类型
    ["setntype"] = function(t)
        udtu.ntype = tonumber(t[1])
        usr_cfg:import(udtu)
        sys.timerStart(sys.restart, 3000, "Set pram reboot!")
        return "rrpc,setntype,OK"
    end, -- 设置网络类型
    ["getntype"] = function(t) return "rrpc,getntype," .. udtu.ntype end, -- 获取网络类型
    ["setdsn"] = function(t)
        udtu.dsn = tostring(t[1])
        usr_cfg:import(udtu)
        sys.timerStart(sys.restart, 3000, "Set pram reboot!")
        return "rrpc,setdsn,OK"
    end, -- 设置设备编号
    ["getdsn"] = function(t) return "rrpc,getdsn," .. udtu.dsn end, -- 获取设备编号    
    ["setnsn"] = function(t)
        udtu.nsn = tostring(t[1])
        usr_cfg:import(udtu)
        sys.timerStart(sys.restart, 3000, "Set pram reboot!")
        return "rrpc,setnsn,OK"
    end, -- 设置设备编号
    ["getnsn"] = function(t) return "rrpc,getnsn," .. udtu.nsn end, -- 获取网络编号
    ["setontime"] = function(t) -- 开机时间设置
        udtu.ontime = tonumber(t[1])
        usr_cfg:import(udtu)
        sys.timerStart(sys.restart, 3000, "Set pram reboot!")
        return "rrpc,setontime,OK"
    end, 
    ["getontime"] = function(t) return "rrpc,getontime," .. udtu.ontime end, -- 获取开机时间
    ["setalarm"] = function(t) -- 设置定时闹钟
        udtu.alarm[1] = tonumber(t[1])
        udtu.alarm[2] = tonumber(t[2])
        usr_cfg:import(udtu)
        sys.timerStart(sys.restart, 3000, "Set pram reboot!")
        return "rrpc,setalarm,OK"
    end, 
    ["getalarm"] = function(t) return string.format("rrpc,getalarm,%d,%d", udtu.alarm[1], udtu.alarm[2]) end, -- 获取定时闹钟
    ["getvmain"] = function(t) return string.format("rrpc,getvmain,%.2f", power.getADC(2) * 11 / 1000) end, -- 获取电源电压
    ["openboost"] = function(t) -- 打开升压
        power.openBoost()
        return "openboost,OK"
    end,
    ["closeboost"] = function(t)
        power.closeBoost()
        return "closeboost,OK"
    end,
    ["setQC"] = function(t)
        power.SET_QC(tonumber(t[1]),tonumber(t[2]))
        return "rrpc,setQC," .. tonumber(t[1]) .. ",OK"
    end,
    ["getQC"] = function(t)
        return "rrpc,getQC," .. power.GET_QC(tonumber(t[1])) .. ",OK"
    end,
    ["opensw"] = function(t)
        if tonumber(t[1]) == 1 then
            power.openSW1()
        elseif tonumber(t[1]) == 2 then
            power.openSW2()
        end
        return "rrpc,opensw," .. tonumber(t[1]) .. ",OK"
    end,
    ["closesw"] = function(t)
        if tonumber(t[1]) == 1 then
            power.closeSW1()
        elseif tonumber(t[1]) == 2 then
            power.closeSW2()
        end
        return "rrpc,closesw," .. tonumber(t[1]) .. ",OK"
    end,
    ["poweroff"] = function(t)
        sys.publish("RTU_POWER_OFF")
        return "rrpc,poweroff,OK"
    end,
    ["getproto"] = function(t)
        if t[1] == dtu.password or dtu.password == "" or dtu.password == nil then
            return udtu.proto or "NOSET"
        else
            return "PASSWORD ERROR"
        end
    end,
    ["sysupdate"] = function(t)
        if t[1] == dtu.password or dtu.password == "" or dtu.password == nil then
            lcd.LCD_DATA[3]= "固件升级中..."
            sys.taskInit(function ()
                log.info("----- update firmware:", "start!")
                update.request(function(res) sys.publish("IRTU_UPDATE_RES", res == true) end, nil, nil)
                local rst, val = sys.waitUntil("IRTU_UPDATE_RES")
                log.info("update",rst,val)
                rst = rst and val --
                if rst then sys.restart("DTU Parameters or firmware are updated!") end
            end)
            return "update firmware start!"
        else
            return "PASSWORD ERROR"
        end
    end,
    ["setproto"] = function(t)
        local pwd = table.remove(t, 1)
        if pwd == dtu.password or dtu.password == "" or dtu.password == nil then
            local funStr = table.concat(t, ",");
            if funStr and  funStr:match("function(.+)end") then
                usr_cfg:update("proto", funStr, true)
                sys.timerStart(sys.restart, 3000, "Remote reboot!")
                return "rrpc,setproto,OK"
            else
                return "rrpc,setproto,ERROR"
            end
        else
            return "PASSWORD ERROR"
        end
    end,
    ["setlcd"] = function(t) --设置回显
        lcd.LCD_DATA[3]= t[1]
        return "setlcd,OK"
    end,
    -----------------------------自定义命令结束--------------
    ["getfwver"] = function(t) return "rrpc,getfwver," .. _G.PROJECT .. "_" .. _G.VERSION .. "_" .. rtos.get_version().."_"..power.getFlag() end,
    ["getnetmode"] = function(t) return "rrpc,getnetmode," .. net.getNetMode() and net.getNetMode() or 1 end,
    ["getver"] = function(t) return "rrpc,getver," .. _G.VERSION end,
    ["getcsq"] = function(t) return "rrpc,getcsq," .. (net.getRssi() or "error ") end,
    ["getadc"] = function(t) return "rrpc,getadc," .. create.getADC(tonumber(t[1]) or 0) end,
    ["setchannel"] = function(t)
        cfg:update("defchan", tonumber((tonumber(t[1]) or 1)), true)
        return "rrpc,setchannel," .. create.setchannel(tonumber(t[1]) or 1)
    end,
    ["reboot"] = function(t)
        sys.timerStart(sys.restart, 1000, "Remote reboot!")
        return "reboot OK!"
    end,
    ["getimei"] = function(t) return "rrpc,getimei," .. (misc.getImei() or "error") end,
    ["getmuid"] = function(t) return "rrpc,getmuid," .. (misc.getMuid() or "error") end,
    ["getimsi"] = function(t) return "rrpc,getimsi," .. (sim.getImsi() or "error") end,
    ["getvbatt"] = function(t) return "rrpc,getvbatt," .. misc.getVbatt() end,
    ["geticcid"] = function(t) return "rrpc,geticcid," .. (sim.getIccid() or "error") end,
    ["getproject"] = function(t) return "rrpc,getproject," .. _G.PROJECT end,
    ["getcorever"] = function(t) return "rrpc,getcorever," .. rtos.get_version() end,
    ["getlocation"] = function(t) return "rrpc,location," .. (lbs.lat or 0) .. "," .. (lbs.lng or 0) end,
    ------------------此处有bug，暂时屏蔽----------
    --[[ ["getreallocation"] = function(t)
        lbsLoc.request(function(result, lat, lng, addr)
            if result then
                lbs.lat, lbs.lng = lat, lng
                create.setLocation(lat, lng)
            end
        end)
        return "rrpc,location," .. (lbs.lat or 0) .. "," .. (lbs.lng or 0)
    end, ]]
    ["gettime"] = function(t)
        local c = misc.getClock()
        return "rrpc,nettime," .. string.format("%04d-%02d-%02d %02d:%02d:%02d", c.year, c.month, c.day, c.hour, c.min, c.sec)
    end,
    ["settime"] = function(t)
        --misc.setClock({year=2017,month=2,day=14,hour=14,min=2,sec=58})
        local data,time = t[1], {}
        if not data or #data~=12 then
            return "rrpc,nettime,ERROR"
        end
        time.year = tonumber(data:sub(1,2))+2000
        time.month = tonumber(data:sub(3,4))
        time.day = tonumber(data:sub(5,6))
        time.hour = tonumber(data:sub(7,8))
        time.min = tonumber(data:sub(9,10))
        time.sec = tonumber(data:sub(11,12))
        misc.setClock(time)
        sys.publish("DTU_SETTIME",time)
        local c = misc.getClock()
        return "rrpc,nettime," .. string.format("%04d-%02d-%02d %02d:%02d:%02d", c.year, c.month, c.day, c.hour, c.min, c.sec)
    end,
    ["setpio"] = function(t)
        if pios["pio" .. t[1]] then
            pios["pio" .. t[1]](tonumber(t[2]) or 0)
            return "OK"
        end
        return "ERROR"
    end,
    ["getpio"] = function(t)
        if pios["pio" .. t[1]] then return "rrpc,getpio" .. t[1] .. "," .. pios["pio" .. t[1]]() end
        return "ERROR"
    end,
    ["getsht"] = function(t)
        local tmp, hum = iic.sht(2, tonumber(t[1]))
        return "rrpc,getsht," .. (tmp or 0) .. "," .. (hum or 0)
    end,
    ["getam2320"] = function(t)
        local tmp, hum = iic.am2320(2, tonumber(t[1]))
        return "rrpc,getam2320," .. (tmp or 0) .. "," .. (hum or 0)
    end,
    ["netstatus"] = function(t)
        if t == nil or t == "" or t[1] == nil or t[1] == "" then
            return "rrpc,netstatus," .. (create.getDatalink() and "RDY" or "NORDY")
        else
            return "rrpc,netstatus," .. (t[1] and (t[1] .. ",") or "") .. (create.getDatalink(tonumber(t[1])) and "RDY" or "NORDY")
        end
    end,
    ["gps_wakeup"] = function(t)
        sys.publish("REMOTE_WAKEUP")
        return "rrpc,gps_wakeup,OK"
    end,
    ["gps_getsta"] = function(t) return "rrpc,gps_getsta," .. tracker.deviceMessage(t[1] or "json") end,
    ["gps_getmsg"] = function(t) return "rrpc,gps_getmsg," .. tracker.locateMessage(t[1] or "json") end,
    ["upconfig"] = function(t)
        sys.publish("UPDATE_DTU_CNF")
        return "rrpc,upconfig,OK"
    end,
    ["function"] = function(t)
        log.info("rrpc,function:", table.concat(t, ","))
        return "rrpc,function," .. (loadstring(table.concat(t, ","))() or "OK")
    end,
    ["tts_play"] = function(t)
        if not isTTS then return "rrpc,tts_play,not_tts_lod" end
        local str = string.upper(t[1]) == "GB2312" and common.gb2312ToUtf8(t[2]) or t[2]
        audio.play(1, "TTS", str, tonumber(t[3]) or 7, nil, false, 0)
        return "rrpc,tts_play,OK"
    end
}
--- 用户串口和远程调用的API接口
-- @string str：执行API的命令字符串
-- @retrun str : 处理结果字符串
function create.userapi(str)
    local t = str:match("(.+)\r\n") and str:match("(.+)\r\n"):split(',') or str:split(',')
    local first = table.remove(t, 1)
    local second = table.remove(t, 1) or ""
    if tonumber(second) and tonumber(second) > 0 and tonumber(second) < 8 then
        return cmd[first]["pipe"](t, second) .. "\r\n"
    elseif cmd[first][second] then
        local res = cmd[first][second](t, str)
        if res and #res>0 then return res.. "\r\n" end
        return res
    else
        return "ERROR\r\n"
    end
end
-- 串口读指令
local function read(uid, idx)
    log.warn("uart.read--->", uid, idx)
    local s = table.concat(recvBuff[idx])
    recvBuff[idx] = {}
    -- 串口流量统计
    --flowCount[idx] = flowCount[idx] + #s
    log.info("UART_" .. uid .. " read:", #s, (s:sub(1, 100):toHex()))
    -- 根据透传标志位判断是否解析数据
    if s:sub(1, 3) == "+++" or s:sub(1, 5):match("(.+)\r\n") == "+++" then
        write(uid, "OK\r\n")
        db.remove(cfg)
        if io.exists("/alikey.cnf") then os.remove("/alikey.cnf") end
        if io.exists("/qqiot.dat") then os.remove("/qqiot.dat") end
        if io.exists("/bdiot.dat") then os.remove("/bdiot.dat") end
        sys.restart("Restore default parameters:", "OK")
    end
    -- DTU的参数配置
    if s:sub(1, 7) == "config," or s:sub(1, 5) == "rrpc," then return write(uid, create.userapi(s)) end
    -- 执行单次HTTP指令
    if s:sub(1, 5) == "http," then
        local str = ""
        local idx1, idx2, jsonstr = s:find(",[\'\"](.+)[\'\"],")
        if jsonstr then
            str = s:sub(1, idx1) .. s:sub(idx2, -1)
        else
            -- 判是不是json，如果不是json，则是普通的字符串
            idx1, idx2, jsonstr = s:find(",([%[{].+[%]}]),")
            if jsonstr then
                str = s:sub(1, idx1) .. s:sub(idx2, -1)
            else
                str = s
            end
        end
        local t = str:match("(.+)\r\n") and str:match("(.+)\r\n"):split(',') or str:split(',')
        if not socket.isReady() then
            write(uid, "NET_NORDY\r\n")
            return
        end
        sys.taskInit(function(t, uid)
            local code, head, body = httpv2.request(t[2]:upper(), t[3], (t[4] or 10) * 1000, nil, jsonstr or t[5], tonumber(t[6]) or 1, t[7], t[8])
            log.info("uart http response:", body)
            write(uid, body)
        end, t, uid)
        return
    end
    -- 执行单次SOCKET透传指令
    if s:sub(1, 4):upper() == "TCP," or s:sub(1, 4):upper() == "UDP," then
        -- local t = s:match("(.+)\r\n") and s:match("(.+)\r\n"):split(',') or s:split(',')
        s = s:match("(.+)\r\n") or s
        if not socket.isReady() then
            write(uid, "NET_NORDY\r\n")
            return
        end
        sys.taskInit(function(uid, prot, ip, port, ssl, timeout, data)
            local c = prot:upper() == "TCP" and socket.tcp(ssl and ssl:lower() == "ssl") or socket.udp()
            while not c:connect(ip, port) do sys.wait(2000) end
            if c:send(data) then
                write(uid, "SEND_OK\r\n")
                local r, s = c:recv(timeout * 1000)
                if r then write(uid, s) end
            else
                write(uid, "SEND_ERR\r\n")
            end
            c:close()
        end, uid, s:match("(.-),(.-),(.-),(.-),(.-),(.+)"))
        return
    end
    -- 非命令字符，发布串口收到消息
    sys.publish("UART_READ_DONE_NEXT_" .. uid, s)
    -- 透传模式 添加设备识别码
    if tonumber(dtu.passon) == 1 then
        local interval, samptime = create.getTimParam()
        if interval[uid] > 0 then -- 定时采集透传模式
            -- 这里注意间隔时长等于预设间隔时长的时候就要采集,否则1秒的采集无法采集
            if os.difftime(os.time(), startTime[uid]) >= interval[uid] then
                if os.difftime(os.time(), startTime[uid]) < interval[uid] + samptime[uid] then
                    table.insert(sendBuff[uid], s)
                elseif startTime[uid] == 0 then
                    -- 首次上电立刻采集1次
                    table.insert(sendBuff[uid], s)
                    startTime[uid] = os.time() - interval[uid]
                else
                    startTime[uid] = os.time()
                    if #sendBuff[uid] ~= 0 then
                        sys.publish("NET_SENT_RDY_" .. uid, tonumber(dtu.plate) == 1 and misc.getImei() .. table.concat(sendBuff[uid]) or table.concat(sendBuff[uid]))
                        sendBuff[uid] = {}
                    end
                end
            else
                sendBuff[uid] = {}
            end
        else -- 正常透传模式
            sys.publish("NET_SENT_RDY_" .. uid, tonumber(dtu.plate) == 1 and misc.getImei() .. s or s)
        end
    else -- 非透传模式,解析数据是否是发送网络数据
        if s:sub(1, 5) == "send," then
            sys.publish("NET_SENT_RDY_" .. s:sub(6, 6), s:sub(8, -1))
        end
    end
end
-- uart 的初始化配置函数
-- 数据流模式
local streamlength = 0
local function streamEnd(uid)
    if #recvBuff[uid] > 0 then
        sys.publish("NET_SENT_RDY_" .. uid, table.concat(recvBuff[uid]))
        recvBuff[uid] = {}
        streamlength = 0
    end
end
function uart_INIT(i, uconf)
    uconf[i][1] = tonumber(uconf[i][1])
    uart.setup(uconf[i][1], uconf[i][2], uconf[i][3], uconf[i][4], uconf[i][5], nil, 1)
    uart.on(uconf[i][1], "sent", writeDone)
    if uconf[i][1] == uart.USB or tonumber(dtu.uartReadTime) > 0 then
        uart.on(uconf[i][1], "receive", function(uid, length)
            table.insert(recvBuff[i], uart.read(uconf[i][1], length or 8192))
            sys.timerStart(sys.publish, tonumber(dtu.uartReadTime) or 25, "UART_RECV_WAIT_" .. uconf[i][1], uconf[i][1], i)
        end)
    else
        uart.on(uconf[i][1], "receive", function(uid, length)
            local str = uart.read(uconf[i][1], length or 8192)
            sys.timerStart(streamEnd, 1000, i)
            streamlength = streamlength + #str
            table.insert(recvBuff[i], str)
            if streamlength > 29200 then
                sys.publish("NET_SENT_RDY_" .. uconf[i][1], table.concat(recvBuff[i]))
                recvBuff[i] = {}
                streamlength = 0
            end
        end)
    end
    -- 处理串口接收到的数据
    
    sys.subscribe("UART_RECV_WAIT_" .. uconf[i][1], read)
    sys.subscribe("UART_SENT_RDY_" .. uconf[i][1], write)
    -- 网络数据写串口延时分帧
    if tonumber(dtu.passon) == 1 then
        sys.subscribe("NET_RECV_WAIT_" .. uconf[i][1], function(uid, str)
            if tonumber(dtu.netReadTime) and tonumber(dtu.netReadTime) > 5 then
                for j = 1, #str, SENDSIZE do table.insert(writeBuff[uid], str:sub(j, j + SENDSIZE - 1)) end
                sys.timerStart(sys.publish, tonumber(dtu.netReadTime) or 30, "UART_SENT_RDY_" .. uid, uid, true)
            else
                sys.publish("UART_SENT_RDY_" .. uid, uid, str)
            end
        end)
    end
    -- 485方向控制
    if pios[dtu.uconf[i][6]] then
        default["dir" .. i] = tonumber(dtu.uconf[i][6]:sub(4, -1))
        pios[dtu.uconf[i][6]] = nil
    else
        default["dir" .. i] = nil
    end
    if default["dir" .. i] then
        pins.setup(default["dir" .. i], 0)
        uart.set_rs485_oe(i, default["dir" .. i])
    end
end
------------------------------------------------ 远程任务 ----------------------------------------------------------
-- 远程自动更新参数和更新固件任务每隔24小时检查一次
sys.taskInit(function()
    local rst, code, head, body, url = false
    while not socket.isReady() do sys.wait(1000) end
    -- 如果是专网就禁止公网操作代码
    if dtu.auth and dtu.auth[2] and tonumber(dtu.auth[1]) then
        sys.wait(5000)
        return sys.publish("DTU_PARAM_READY")
    end
    -- 加载错入日志和远程升级功能
    -- 加载错误日志管理功能模块【强烈建议打开此功能】
    -- 如下2行代码，只是简单的演示如何使用errDump功能，详情参考errDump的api
    errDump.request("udp://ota.airm2m.com:9072")
    ntp.timeSync(24, function() log.info(" AutoTimeSync is Done!") end)
    while true do
        rst = false
        if dtu.host and dtu.host ~= "" then
            local param = {
                product_name = _G.PROJECT,
                param_ver = dtu.param_ver,
                imei = misc.getImei()
            }
            code, head, body = httpv2.request("GET", dtu.host, 30000, param, nil, 1)
        else --屏蔽默认地址的参数检查
            --url = "dtu.openluat.com/api/site/device/" .. misc.getImei() .. "/param?product_name=" .. _G.PROJECT .. "&param_ver=" .. dtu.param_ver
            --code, head, body = httpv2.request("GET", url, 30000, nil, nil, 1, misc.getImei() .. ":" .. misc.getMuid())
        end
        if tonumber(code) == 200 and body then
            -- log.info("Parameters issued from the server:", body)
            local dat, res, err = json.decode(body)
            if res and tonumber(dat.param_ver) ~= tonumber(dtu.param_ver) then
                cfg:import(body)
                rst = true
            end
        end
        -- 检查是否有更新程序
        if tonumber(dtu.fota) == 1 then
            log.info("----- update firmware:", "start!")
            update.request(function(res) sys.publish("IRTU_UPDATE_RES", res == true) end, nil, 86400000)
            local res, val = sys.waitUntil("IRTU_UPDATE_RES")
            rst = rst or val
            log.info("----- update firmware:", "end!")
        end
        if rst then sys.restart("DTU Parameters or firmware are updated!") end
        ---------- 启动网络任务 ----------
        sys.publish("DTU_PARAM_READY")
        sys.wait(20*1000)
        ---------- 基站坐标查询 ----------
        lbsLoc.request(function(result, lat, lng, addr)
            if result then
                lbs.lat, lbs.lng = lat, lng
                create.setLocation(lat, lng)
--[[                 sys.wait(1000)
                log.info("基站定位信息发送服务器:",create.getDatalink(1), lat, lng)
                local cid = tonumber(dtu.passon) == 1 and (dtu.conf[1][6] or 3) or 1
                sys.publish("NET_SENT_RDY_" ..cid,"rrpc,location," .. lat .. "," ..lng) ]]
            end
        end)
        log.warn("短信或电话请求更新:", sys.waitUntil("UPDATE_DTU_CNF", 86400000))
    end
end)
-- 初始化配置UART1、2、3、4
local uidgps = dtu.gps and dtu.gps.fun and tonumber(dtu.gps.fun[1])
if uidgps ~= 1 and dtu.uconf and dtu.uconf[1] and tonumber(dtu.uconf[1][1]) == 1 then uart_INIT(1, dtu.uconf) end
if uidgps ~= 2 and dtu.uconf and dtu.uconf[2] and tonumber(dtu.uconf[2][1]) == 2 then uart_INIT(2, dtu.uconf) end
if uidgps ~= 3 and dtu.uconf and dtu.uconf[3] and tonumber(dtu.uconf[3][1]) == 3 then uart_INIT(3, dtu.uconf) end
if not _G.is8955 then
    dtu.uconf[4] = {uart.USB, 0, 0, uart.PAR_NONE, uart.STOP_1}
    uart_INIT(4, dtu.uconf)
end
-- 启动GPS任务
if uidgps then
    -- 从pios列表去掉自定义的io
    if dtu.gps.pio then for i = 1, 3 do if pios[dtu.gps.pio[i]] then pios[dtu.gps.pio[i]] = nil end end end
    sys.taskInit(tracker.sensMonitor, unpack(dtu.gps.pio))
    sys.taskInit(tracker.alert, unpack(dtu.gps.fun))
end
---------------------------------------------------------- 预警任务线程 ----------------------------------------------------------
if dtu.warn and dtu.warn.gpio and #dtu.warn.gpio > 0 then
    for i = 1, #dtu.warn.gpio do
        pins.setup(tonumber(dtu.warn.gpio[i][1]:sub(4, -1)), function(msg)
            if (msg == cpu.INT_GPIO_NEGEDGE and tonumber(dtu.warn.gpio[i][2]) == 1) or (msg == cpu.INT_GPIO_POSEDGE and tonumber(dtu.warn.gpio[i][3]) == 1) then
                if tonumber(dtu.warn.gpio[i][6]) == 1 then sys.publish("NET_SENT_RDY_" .. dtu.warn.gpio[i][5], dtu.warn.gpio[i][4]) end
                if dtu.preset and tonumber(dtu.preset.number) then
                    if tonumber(dtu.warn.gpio[i][7]) == 1 then sms.send(dtu.preset.number, common.utf8ToGb2312(dtu.warn.gpio[i][4])) end
                    if tonumber(dtu.warn.gpio[i][8]) == 1 then
                        if cc and cc.dial then
                            cc.dial(dtu.preset.number, 5)
                        else
                            ril.request(string.format("%s%s;", "ATD", dtu.preset.number), nil, nil, 5)
                        end
                    end
                end
            end
        end, pio.PULLUP)
    end
end

local function adcWarn(adcid, und, lowv, over, highv, diff, msg, id, sfreq, upfreq, net, note, tel)
    local upcnt, scancnt, adcValue, voltValue = 0, 0, 0, 0
    diff = tonumber(diff) or 1
    lowv = tonumber(lowv) or 1
    highv = tonumber(highv) or 4200
    while true do
        -- 获取ADC采样电压
        scancnt = scancnt + 1
        if scancnt == tonumber(sfreq) then
            if adcid == 0 or adcid == 1 then
                adc.open(adcid)
                adcValue, voltValue = adc.read(adcid)
                if adcValue ~= 0xFFFF or voltValue ~= 0xFFFF then voltValue = (voltValue - voltValue % 3) / 3 end
                adc.close(adcid)
            else
                voltValue = misc.getVbatt()
            end
            scancnt = 0
        end
        -- 处理上报
        if ((tonumber(und) == 1 and voltValue < tonumber(lowv)) or (tonumber(over) == 1 and voltValue > tonumber(highv))) then
            if upcnt == 0 then
                if tonumber(net) == 1 then sys.publish("NET_SENT_RDY_" .. id, msg) end
                if tonumber(note) == 1 and dtu.preset and tonumber(dtu.preset.number) then sms.send(dtu.preset.number, common.utf8ToGb2312(msg)) end
                if tonumber(tel) == 1 and dtu.preset and tonumber(dtu.preset.number) then
                    if cc and cc.dial then
                        cc.dial(dtu.preset.number, 5)
                    else
                        ril.request(string.format("%s%s;", "ATD", dtu.preset.number), nil, nil, 5)
                    end
                end
                upcnt = tonumber(upfreq)
            else
                upcnt = upcnt - 1
            end
        end
        -- 解除警报
        if voltValue > tonumber(lowv) + tonumber(diff) and voltValue < tonumber(highv) - tonumber(diff) then upcnt = 0 end
        sys.wait(1000)
    end
end
if dtu.warn and dtu.warn.adc0 and dtu.warn.adc0[1] then sys.taskInit(adcWarn, 0, unpack(dtu.warn.adc0)) end
if dtu.warn and dtu.warn.adc1 and dtu.warn.adc1[1] then sys.taskInit(adcWarn, 1, unpack(dtu.warn.adc1)) end
if dtu.warn and dtu.warn.vbatt and dtu.warn.vbatt[1] then sys.taskInit(adcWarn, 9, unpack(dtu.warn.vbatt)) end
---------------------------------------------------------- 参数配置,任务转发，线程守护主进程----------------------------------------------------------
sys.taskInit(create.connect, pios, dtu.conf, dtu.reg, tonumber(dtu.convert) or 0, (tonumber(dtu.passon) == 1), dtu.upprot, dtu.dwprot)

---------------------------------------------------------- 用户自定义任务初始化 ---------------------------------------------------------
if dtu.task and #dtu.task ~= 0 then for i = 1, #dtu.task do if dtu.task[i] and dtu.task[i]:match("function(.+)end") then sys.taskInit(loadstring(dtu.task[i]:match("function(.+)end"))) end end end

udtu.ntype = tonumber(udtu.ntype)
udtu.dtype = tonumber(udtu.dtype)
--- 准备网络协议解析
if udtu.ntype == 1 then
     require "szy206"        --1,szy206
    szy206.setDtu(dtu, udtu)
elseif udtu.ntype == 2 then
    require "automic1"        --2,automic1
    automic1.setDtu(dtu, udtu)
end
-- 准备设备解析协议
lcd.setDtu(dtu, udtu)
-- 开启MCGS 串口屏交互
if udtu.mcgs and udtu.mcgs == 1 then
    require "MCGS"
    require "modbus_slave"
    MCGS.setDtu(dtu, udtu)
end
if udtu.dtype == 0 then
    power.openBoost()
    lcd.LCD_DATA[3]= "UART3 透传模式"
elseif udtu.dtype == 1 then
    require "ll485"         --1,连利水表光电
    ll485.setDtu(dtu, udtu)
elseif udtu.dtype == 2 then
    require "tuf2000"       --2,海峰流量计
    tuf2000.setDtu(dtu, udtu)
elseif udtu.dtype == 3 then
    require "dldsT3_1"      --3,道胜水表
    dldsT3_1.setDtu(dtu, udtu)
elseif udtu.dtype == 4 then
    require "LXSY_adr01"    --4,山东晨硕光电直读水表
    LXSY_adr01.setDtu(dtu, udtu)
elseif udtu.dtype == 5 then
    require "l_mag"         --5,L-mag电磁流量计/晨硕/恒瑞HR7100通用
    l_mag.setDtu(dtu, udtu)
elseif udtu.dtype == 6 then
    require "chenshuo_csb"  --6,晨硕超声波流量计-管段
    chenshuo_csb.setDtu(dtu, udtu)
elseif udtu.dtype == 7 then
    require "KT_DC"         --7,肯特 插入式电磁流量计
    KT_DC.setDtu(dtu, udtu)
elseif udtu.dtype == 8 then
    require "MB_mag"        --8,开封 MB_mag
    MB_mag.setDtu(dtu, udtu)
elseif udtu.dtype == 9 then
    require "XT_MOD"        --9,新天modbus
    XT_MOD.setDtu(dtu, udtu)
elseif udtu.dtype == 12 then
    require "XT_188"        --12,新天188
    XT_188.setDtu(dtu, udtu)
elseif udtu.dtype == 13 then
    require "dldsT3_2"      --13,道胜热量表
    dldsT3_2.setDtu(dtu, udtu)
elseif udtu.dtype == 14 then
    require "XT_GDZD"       --14,新天光电modbus
    XT_GDZD.setDtu(dtu, udtu)
elseif udtu.dtype == 15 then
    require "SCL_6X_8X"     --15,汇中_SCL_6x
    SCL_6X_8X.setDtu(dtu,udtu)
elseif udtu.dtype == 16 then
    require "SCL_61D"       --16,汇中_SCL_61D
    SCL_61D.setDtu(dtu,udtu)
elseif udtu.dtype == 17 then
    require "SDZR_CSB"      --17,山东泽润超声波
    SDZR_CSB.setDtu(dtu,udtu)
elseif udtu.dtype == 18 then
    require "XXHL_DC"       --18,新乡华隆插入式
    XXHL_DC.setDtu(dtu,udtu)
elseif udtu.dtype == 19 then
    require "CQCY_DC"       --19,重庆川仪电磁流量计
    CQCY_DC.setDtu(dtu,udtu)
elseif udtu.dtype == 11 then
    require "hr8xxx"        --11,恒瑞水位传感器
    hr8xxx.setDtu(dtu, udtu)
elseif udtu.dtype == 20 then
    require "HR7360"        --20,恒瑞河流雷达流量计
    HR7360.setDtu(dtu,udtu)
elseif udtu.dtype == 21 then
    require "AHTW"        --21,安徽天维
    AHTW.setDtu(dtu,udtu)
elseif udtu.dtype == 22 then
    require "GF9900"        --22,GF9900
    GF9900.setDtu(dtu,udtu)
elseif udtu.dtype == 23 then
    require "tuf2000_jj"    --23,TUF2000净积
    tuf2000_jj.setDtu(dtu,udtu)
elseif udtu.dtype == 24 then
    require "SIEMENS"    --24,SIEMENS
    SIEMENS.setDtu(dtu,udtu)
elseif udtu.dtype == 25 then
    require "SCL_61D_hzxx"   --25,汇中_汇中协议
    SCL_61D_hzxx.setDtu(dtu,udtu)
    SIEMENS.setDtu(dtu,udtu)
elseif udtu.dtype == 26 then
    require "HZSVR24Q"   --26,航征_24Q
    HZSVR24Q.setDtu(dtu,udtu)
elseif udtu.dtype == 27 then
    require "river"     --27,河流雨量/水位
    river.setDtu(dtu,udtu)
elseif udtu.dtype == 28 then
    require "SZJH"   --28,深圳建恒
    SZJH.setDtu(dtu,udtu)
elseif udtu.dtype == 99 then
    require "MODBUS_G"      --99,modbus通用协议
    MODBUS_G.setDtu(dtu,udtu)
elseif udtu.dtype==200 then
    require "QT01"         --200,气体检测
    QT01.setDtu(dtu, udtu)
elseif udtu.dtype==202 then
    require "QT02"         --202,上海松柏气体检测
    QT02.setDtu(dtu, udtu)
elseif udtu.dtype==203 then
    require "SHT30"         --203,SHT30温湿度
    SHT30.setDtu(dtu, udtu)
elseif udtu.dtype == 10 then --10,自定义协议解析
    protoFun = udtu.proto and loadstring(udtu.proto:match("function(.+)end"))
    if protoFun then
        sys.taskInit(protoFun, {dtu, udtu})
    else
        log.error("default", "自定义解析协议无效")
        lcd.LCD_DATA[3]= "自定义解析协议无效"
    end
end
-- 发送数据到网络1
function send_data_server()
    usrdata.push()
    usrdata.IS_UP_OK_1 = false
    lcd.USR_STATUS = ' UP'
    local status = string.format("%d,%0.2f,dat=", net.getRssi(), power.getVbat())
    local len = usrdata.size()
    if len == 0 then return end
    for i = 1, len, 10 do
        local temp = {unpack(usrdata.get(), i, i + 9)}
        --log.warn("default","上报数据:",i,len,"NET_SENT_RDY_" .. (dtu.defchan or 1),json.encode(temp))
        sys.publish("NET_SENT_RDY_" .. (dtu.defchan or 1), "rrpc,upusrdata," .. status .. json.encode(temp) .. '\r\n')
        if (i + 10) <= len then sys.wait(500) end --防止等待后接收不到消息
    end
    lcd.USR_STATUS = ' UP...'
    local sendOK = sys.waitUntil("DTU_USRDATA_ISSEND", 20 * 1000)
    if sendOK then
        usrdata.IS_UP_OK_1 = true
        usrdata.clear()
        if not create.getDatalink(3) or usrdata.IS_UP_OK_3 then
            sys.publish("RTU_POWER_OFF")
        end --防止有第三方协议时关机过快
    else
        lcd.USR_STATUS = ' ERR'
    end
end
-- 设备启动时上报
sys.taskInit(function()
    local isConn = sys.waitUntil("DTU_CONNECTED_" .. (dtu.defchan or 1), 30 * 1000)
    -- 等待读取数据
    local hasData, data = sys.waitUntil("USRDATA_UPDATE", 20 * 1000)
    if hasData then
        send_data_server()
    else
        if isConn then
            sys.publish("NET_SENT_RDY_" .. (dtu.defchan or 1), "read_device_error") -- 读取表失败
        end
    end
end)
-- 闹钟触发上报
sys.subscribe("DTU_ALARM_ON", function()
    sys.publish("CLEAR_COUNT",0)
    lcd.USR_STATUS = ' ALMON'
    sys.taskInit(send_data_server)
end) 
sys.subscribe("DTU_USRDATA_START", function()
    sys.taskInit(function ( )
        sys.wait(1000) --- 返回后才能轮询到发送数据任务
        send_data_server()
    end)
end) 
sys.subscribe("DTU_USRDATA_ISSEND", function()
    lcd.USR_STATUS = ' UPOK'
 end)
-- sys.subscribe("USRDATA_UPDATE", function() lcd.USR_STATUS = ' READOK' end)
sys.subscribe("PCF_SETTIME", setClock) -- 校时后重设闹钟
if udtu.rest and tonumber(udtu.rest)>0 then
    sys.timerLoopStart(sys.restart,tonumber(udtu.rest) * 60 * 60 * 1000,'Timer restart') 
end
